package com.jeady.jxcompose

import android.os.Looper
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.google.gson.Gson
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import java.io.FileOutputStream
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread

private val TAG = "[ServerManager]"
data class ServerData(
    @JvmField var id: java.lang.Double = java.lang.Double(-1.0),
    @JvmField var name: String="",
    @JvmField var gender: String="",
    @JvmField var phone: String=""
){
    constructor(src: Map<*, *>) : this() {
        this.javaClass.fields.forEach { field->
            if(!field.name.startsWith("$")){
                if(src[field.name]?.javaClass == field.type){
                    field.set(this, src[field.name])
                }
            }
        }
    }
}
@Composable
fun TestServerManager() {
    val result = remember {
        mutableStateListOf<ServerData>()
    }
    var errMessage by remember{ mutableStateOf("") }
    Column(
        Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ){
        Button(onClick = {
            thread {
                ServerManager.getInstance("http://192.168.31.31:3000")
                    .get("users","", List::class.java) { data, errMsg->
                        Log.i(TAG, "TestServerManager: get data res: $data ; \nerr:$errMsg")
                        if(data==null){
                            errMessage = errMsg?:""
                        }
                        data?.forEach{ result.add(ServerData(it as Map<*, *>)) }
                    }
            }
        },
            Modifier
                .align(Alignment.End)
                .padding(20.dp)
        ) {
            Text(text = "Get")
        }
        Button(onClick = {
            thread {
                ServerManager.getInstance("http://192.168.31.31:3000")
                    .post("users","{\"name\": \"xx\"}", Number::class.java) { data, errMsg->
                        Log.i(TAG, "TestServerManager: post data res: $data; \nerr: $errMsg")
                        if(data==null){
                            errMessage = errMsg?:""
                        }else{
                            errMessage = "get post res: $data"
                        }
                    }
            }
        },
            Modifier
                .align(Alignment.End)
                .padding(20.dp)
        ) {
            Text(text = "Post")
        }
        Text(text = "查询结果", fontWeight = FontWeight.SemiBold)
        Text(text = errMessage)
        LazyColumn{
            itemsIndexed(result){idx, it->
                Row(
                    Modifier
                        .fillMaxWidth()
                        .background(if (idx % 2 == 0) Color.White else Color.LightGray),
                    horizontalArrangement = Arrangement.Center
                ) {
                    Text(text = it.id.toString(), Modifier.width(200.dp), textAlign = TextAlign.Center)
                    Text(text = it.name, Modifier.width(200.dp), textAlign = TextAlign.Center)
                    Text(text = it.gender, Modifier.width(200.dp), textAlign = TextAlign.Center)
                    Text(text = it.phone, Modifier.width(200.dp), textAlign = TextAlign.Center)
                }
            }
        }
    }
}

/** 基于OKHttp的接口封装
 * @sample ServerManager.getInstance("https://example.com")
 */
class ServerManager(var baseUrl: String, var timeout: Long=10) {
    private val TAG = "[ServerManager] "
    private var client: OkHttpClient = OkHttpClient.Builder()
        .readTimeout(timeout, TimeUnit.SECONDS)
        .writeTimeout(timeout, TimeUnit.SECONDS)
        .callTimeout(timeout, TimeUnit.SECONDS)
        .build()

    companion object {
        private lateinit var instance: ServerManager
        private fun initInstance(url: String, timeout: Long){
            if(!Companion::instance.isInitialized){
                instance = ServerManager(url, timeout)
            }
            instance.baseUrl = url
            instance.timeout = timeout
        }

        /**
         * Get instance of ServerManager
         * @param baseUrl ex: http://localhost:3000
         * @return serverManager
         */
        fun getInstance(baseUrl: String, timeout: Long=10): ServerManager {
            initInstance(baseUrl.trimEnd('/'), timeout)
            return instance
        }
    }

    /**
     * 发起GET请求并返回 T 类型
     */
    fun <T> get(path: String="", query: String="", classType: Class<T>?=null, onResult: (res: T?, errMsg: String?)->Unit){
        val targetUrl = baseUrl.let {
            if(path.trim('/').isNotEmpty()){
                return@let "$it/$path"
            }
            it
        }.let {
            if(query.trim('?').isNotEmpty()){
                return@let "$it?$query"
            }
            it
        }
        Log.w(TAG, "get: get target url $targetUrl")
        val request = Request.Builder()
            .url(targetUrl)
            .build()
        thread {
            Looper.prepare()
            val response: Response
            try {
                response = client.newCall(request).execute()
            }catch (e: Exception){
                onResult(null, e.message)
                return@thread
            }
            try {
                val result = handleResult(response.body?.string(), classType)
                onResult(result, response.message)
            }catch (e: Exception){
                onResult(null, e.toString())
            }
        }
    }
    /**
     * 发起GET请求并下载其内容
     */
    fun download(path: String="", query: String="", downloadPath: String, onResult: (state: String?)->Unit){
        val targetUrl = baseUrl.let {
            if(path.trim('/').isNotEmpty()){
                return@let "$it/$path"
            }
            it
        }.let {
            if(query.trim('?').isNotEmpty()){
                return@let "$it?$query"
            }
            it
        }
        Log.w(TAG, "get: get target url $targetUrl")
        val request = Request.Builder()
            .url(targetUrl)
            .build()
        thread {
            Looper.prepare()
            val response: Response
            try {
                response = client.newCall(request).execute()
                response.body?.let {body->
                    body.byteStream().use {inStream->
                        val fos = FileOutputStream(downloadPath)
                        fos.use {
                            val buffer = ByteArray(1024)
                            val total = body.contentLength()
                            var len = 0
                            while (true){
                                len = inStream.read(buffer)
                                if(len==-1){
                                    break
                                }
                                fos.write(buffer, 0, len)
                                onResult("$len/$total")
                            }
                        }
                    }
                }
            }catch (e: Exception){
                onResult(e.localizedMessage)
                return@thread
            }
        }
    }

    /**
     * 发起POST请求并返回 T 类型
     */
    fun <T> post(path: String="", body: String="", classType: Class<T>?=null, onResult: (T?, String?)->Unit){
        val targetUrl = baseUrl.let {
            val pathCat = path.trim('/')
            if(pathCat.isNotEmpty()){
                return@let "$it/$pathCat"
            }
            it
        }
        Log.w(TAG, "post: post target url $targetUrl ，body: $body")
        val requestBody = body.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
        val request = Request.Builder().url(targetUrl)
            .post(requestBody)
            .build()
        thread {
            Looper.prepare()
            val response: Response
            try {
                response = client.newCall(request).execute()
            }catch (e: Exception){
                Log.e(TAG, "post: cat exception $e", )
                onResult(null, e.message)
                return@thread
            }
            val bodyStr = response.body?.string()
            val result = handleResult(bodyStr, classType)
//            Log.i(TAG, "post: response ${response.code} ${response.isSuccessful}, ${response.message}", )
            if(response.isSuccessful){
                onResult(result, null)
            }else{
                onResult(result, response.message)
            }
        }
    }

    private fun <V>handleResult(body: String?, type: Class<V>?): V? {
//        Log.w(TAG, "handleResult: result $body", )
        if(type==null){
            return null
        }else if(type == String::class.java){
            return body as? V
        }
        return body?.let {content->
            try {
                val json = Gson().fromJson(content, type)
//                Log.d(TAG, "handleResult: after parse result $json")
                return@let json
            }catch (e: Exception){
                Log.e(TAG, "handleResult: cat err $e", )
                null
            }
        }
    }
}